﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace TDB
{
  /// <summary>
  /// The WatchDog helps you to manage multi domain code and domain faults due to bad code. 
  /// </summary>
  internal static class WatchDog
  {
    private static Dictionary<int, AppDomain> _domaintracker = new Dictionary<int, AppDomain>();
    private static Dictionary<int, Action> _faultedcode = new Dictionary<int, Action>();

    static WatchDog()
    {
      AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
    }

    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
      var ex = e.ExceptionObject as Exception;
      var faulteddomainid = MultiDomainExceptionTracker.GetDomainIdForException(ex);
      if (faulteddomainid > 0)
      {
        if (_domaintracker.ContainsKey(faulteddomainid))
        {
          var codetorun = _faultedcode[faulteddomainid];
          var faulteddomain = _domaintracker[faulteddomainid];

          _faultedcode.Remove(faulteddomainid);
          _domaintracker.Remove(faulteddomainid);

          try
          {
            AppDomain.Unload(faulteddomain);
            if (codetorun != null) codetorun();
          }
          catch { }
        }
      }
    }

    private static void ExecuteInNewAppDomainAndWatch(string appdomainname,
                                       ThreadStart codetorun,
                                       Action faultedstatecallback)
    {
      if (codetorun == null) return;

      AppDomainSetup apsetup = new AppDomainSetup();
      apsetup.ApplicationName = appdomainname;
      apsetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
      AppDomain workerdomain = AppDomain.CreateDomain(appdomainname, null, apsetup);

      workerdomain.UnhandledException += (o, e) =>
      {
        Exception ex = e.ExceptionObject as Exception;
        MultiDomainExceptionTracker.TrackException(ex);
      };

      _domaintracker.Add(workerdomain.Id, workerdomain);
      _faultedcode.Add(workerdomain.Id, faultedstatecallback);

      workerdomain.DoCallBack(() =>
      {
        new Thread(codetorun).Start();
      });

    }

  }
}
